/*
 * Copyright (C) 2018 Zhejiang xiaominfo Technology CO.,LTD.
 * All rights reserved.
 * Official Web Site: http://www.xiaominfo.com.
 * Developer Web Site: http://open.xiaominfo.com.
 */
package com.github.xiaoymin.map.ext;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.github.xiaoymin.map.MapClientConfig;
import com.github.xiaoymin.map.adapter.AmapAddressAdapter;
import com.github.xiaoymin.map.adapter.RemoveNullListDerializer;
import com.github.xiaoymin.map.common.MapClientConstants;
import com.github.xiaoymin.map.model.AmapKey;
import com.github.xiaoymin.map.model.MapLocation;
import com.github.xiaoymin.map.request.amap.AmapCoordConvertRequest;
import com.github.xiaoymin.map.request.amap.AmapDistrictRequest;
import com.github.xiaoymin.map.request.amap.AmapGeoRequest;
import com.github.xiaoymin.map.request.amap.AmapRegeoRequest;
import com.github.xiaoymin.map.response.amap.AmapDistrictResponse;
import com.github.xiaoymin.map.response.amap.AmapIpLocation;
import com.github.xiaoymin.map.response.amap.regeo.AmapRegeoAddressComponent;
import com.github.xiaoymin.map.response.amap.regeo.AmapRegeoBusinessArea;
import com.github.xiaoymin.map.response.amap.regeo.AmapRegeoInfo;
import com.github.xiaoymin.map.response.amap.regeo.AmapRegeoSimpleAddress;
import com.github.xiaoymin.map.util.GsonUtils;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import org.apache.http.client.methods.HttpGet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 高德地图WebApi接口
 * @author <a href="mailto:xiaoymin@foxmail.com">xiaoymin@foxmail.com</a>
 * 2020/09/21 13:21
 * @since:map-client 1.0
 */
public class AmapClient extends AmapAbstractClient {

    Logger logger=LoggerFactory.getLogger(AmapClient.class);

    /**
     * 根据秘钥配置构造高德地图Client
     * @param mapClientConfig 开发配置
     */
    public AmapClient(MapClientConfig mapClientConfig) {
        super(mapClientConfig);
    }

    public void geo(AmapGeoRequest aMapGeoRequest){
    }

    /**
     * 逆地理编码API
     * https://lbs.amap.com/api/webservice/guide/api/georegeo
     * @param amapRegeoRequest 请求参数
     * @return 逆地理编码结果
     */
    public Optional<AmapRegeoInfo> regeo(AmapRegeoRequest amapRegeoRequest){
        assertNotEmpty(amapRegeoRequest,"逆地理编码API请求对象不能为空");
        AmapKey amapKey=mapClientConfig.getAmapKey();
        String api= MapClientConstants.AMAP_API_REGEO+parameter(appendKey(amapRegeoRequest.toMap(),amapKey),amapKey);
        logger.info("逆地理编码API:{}",api);
        HttpGet get=new HttpGet(api);
        String content=execute(get);
        if (StrUtil.isNotBlank(content)){
            JsonElement jsonElement=jsonParser.parse(content);
            if(jsonElement!=null&&jsonElement.isJsonObject()&&jsonElement.getAsJsonObject()!=null){
                JsonObject jsonObject=jsonElement.getAsJsonObject();
                String status= GsonUtils.getString(jsonObject,"status");
                if (StrUtil.equals(status, MapClientConstants.AMAP_RESPONSE_SUCCESS)){
                    JsonObject regeoJsonObject=GsonUtils.getJsonObj(jsonObject,"regeocode");
                    //需要拆开解析各个属性节点,AmapRegeoAddressComponent的city节点有时候存在数组、有时候是字符串
                    GsonBuilder  gsonBuilder=new GsonBuilder();
                    gsonBuilder.registerTypeAdapter(List.class,new RemoveNullListDerializer<AmapRegeoBusinessArea>());
                    gsonBuilder.registerTypeAdapter(AmapRegeoAddressComponent.class,new AmapAddressAdapter());
                    AmapRegeoInfo amapRegeoInfo=gsonBuilder.create().fromJson(regeoJsonObject,AmapRegeoInfo.class);
                    return Optional.ofNullable(amapRegeoInfo);
                }
            }
        }
        return Optional.empty();
    }

    /**
     * 根据经纬度坐标获取逆向地址
     * @param lng 经度
     * @param lat 纬度
     * @return 地理位置
     */
    public Optional<AmapRegeoSimpleAddress> regeoSimpleAddress(String lng,String lat){
        assertNotEmpty(lng,"经度坐标不能为空");
        assertNotEmpty(lat,"纬度坐标不能为空");
        AmapRegeoRequest amapRegeoRequest=new AmapRegeoRequest.Builder().addLocation(lng, lat).batch(false).build();
        AmapKey amapKey=mapClientConfig.getAmapKey();
        String api= MapClientConstants.AMAP_API_REGEO+parameter(appendKey(amapRegeoRequest.toMap(),amapKey),amapKey);
        logger.debug("逆地理编码API:{}",api);
        HttpGet get=new HttpGet(api);
        JsonObject jsonObject=responseSuccess(get);
        if (jsonObject!=null){
            JsonObject regeoJsonObject=GsonUtils.getJsonObj(jsonObject,"regeocode");
            JsonObject addressComponent=GsonUtils.getJsonEle(regeoJsonObject,"addressComponent");
            if (regeoJsonObject!=null&&addressComponent!=null){
                String address=GsonUtils.getString(regeoJsonObject,"formatted_address");
                AmapRegeoSimpleAddress amapRegeoSimpleAddress=new AmapRegeoSimpleAddress();
                amapRegeoSimpleAddress.setLng(lng);
                amapRegeoSimpleAddress.setLat(lat);
                amapRegeoSimpleAddress.setAddress(address);
                amapRegeoSimpleAddress.setProvince(GsonUtils.getString(addressComponent,"province"));
                amapRegeoSimpleAddress.setCitycode(GsonUtils.getString(addressComponent,"citycode"));
                amapRegeoSimpleAddress.setDistrict(GsonUtils.getString(addressComponent,"district"));
                amapRegeoSimpleAddress.setTownship(GsonUtils.getString(addressComponent,"township"));
                amapRegeoSimpleAddress.setAdcode(GsonUtils.getString(addressComponent,"adcode"));
                //地级市的情况,上海、北京
                amapRegeoSimpleAddress.setCity(amapRegeoSimpleAddress.getProvince());
                JsonElement cityEle=addressComponent.get("city");
                //判断city类型
                if (cityEle!=null){
                    if(cityEle.isJsonPrimitive()){
                        amapRegeoSimpleAddress.setCity(cityEle.getAsString());
                    }
                }

                return Optional.of(amapRegeoSimpleAddress);
            }
        }
        return Optional.empty();
    }

    /**
     * 坐标系转换
     * @param amapCoordConvertRequest 转换请求
     * @return 一组转换后的坐标系数组
     */
    public List<MapLocation> coordConvert(AmapCoordConvertRequest amapCoordConvertRequest){
        assertNotEmpty(amapCoordConvertRequest,"坐标系转换请求对象不能为空");
        AmapKey amapKey=mapClientConfig.getAmapKey();
        String api= MapClientConstants.AMP_API_COORD_CONVERT+parameter(appendKey(amapCoordConvertRequest.toMap(),amapKey),amapKey);
        logger.debug("坐标系转换API:{}",api);
        HttpGet get=new HttpGet(api);
        JsonObject jsonObject=responseSuccess(get);
        if (jsonObject!=null){
            String locations=GsonUtils.getString(jsonObject,"locations");
            if (StrUtil.isNotBlank(locations)){
                return Arrays.asList(StrUtil.split(locations,";")).stream().map(s -> {
                    String[] lcs=StrUtil.split(s,",");
                    return new MapLocation(lcs[0],lcs[1]);
                }).collect(Collectors.toList());
            }
        }
        return null;
    }

    /**
     * 根据ip地址定位得到地理位置
     * @param ip ip地址
     * @return ip地理位置信息
     */
    public Optional<AmapIpLocation> ipLocation(String ip){
        assertNotEmpty(ip,"ip地址不能为空");
        AmapKey amapKey=mapClientConfig.getAmapKey();
        Map<String,String> params= MapUtil.builder("ip",ip).build();
        String api= MapClientConstants.AMP_API_IP_LOCATION+parameter(appendKey(params,amapKey),amapKey);
        logger.debug("IP地理位置定位API:{}",api);
        HttpGet get=new HttpGet(api);
        JsonObject jsonObject=responseSuccess(get);
        if (jsonObject!=null){
            AmapIpLocation amapIpLocation=new AmapIpLocation();
            String locations=GsonUtils.getString(jsonObject,"rectangle");
            if (StrUtil.isNotBlank(locations)){
                amapIpLocation.setRectangle(Arrays.asList(StrUtil.split(locations,";")).stream().map(s -> {
                    String[] lcs=StrUtil.split(s,",");
                    return new MapLocation(lcs[0],lcs[1]);
                }).collect(Collectors.toList()));
            }
            amapIpLocation.setProvince(GsonUtils.getString(jsonObject,"province"));
            amapIpLocation.setCity(GsonUtils.getString(jsonObject,"city"));
            amapIpLocation.setAdcode(GsonUtils.getString(jsonObject,"adcode"));
            return Optional.of(amapIpLocation);
        }
        return Optional.empty();
    }

    /**
     * 行政区划查询
     * @param amapDistrictRequest 请求参数
     * @return 行政区划集合
     */
    public List<AmapDistrictResponse> district(AmapDistrictRequest amapDistrictRequest){
        assertNotEmpty(amapDistrictRequest,"行政区划请求参数不能为空");
        AmapKey amapKey=mapClientConfig.getAmapKey();
        String api= MapClientConstants.AMP_API_DISTRICT_QUERY+parameter(appendKey(amapDistrictRequest.toMap(),amapKey),amapKey);
        logger.debug("行政区划查询API:{}",api);
        HttpGet get=new HttpGet(api);
        JsonObject jsonObject=responseSuccess(get);
        if (jsonObject!=null){
            JsonObject regeoJsonObject=GsonUtils.getJsonObj(jsonObject,"districts");
            Type type=new TypeToken<List<AmapDistrictResponse>>(){}.getType();
            return gson.fromJson(regeoJsonObject,type);
        }
        return null;

    }
}
